#include "helper.h"
#include "monitor.h"

extern char assembly[80];
extern uint32_t instr;

/* invalid opcode */
make_helper(inv) {

	uint32_t temp;
	temp = instr_fetch(pc, 4);

	uint8_t *p = (void *)&temp;
	printf("invalid opcode(pc = 0x%08x): %02x %02x %02x %02x ...\n\n", 
			pc, p[3], p[2], p[1], p[0]);

	printf("There are two cases which will trigger this unexpected exception:\n\
1. The instruction at pc = 0x%08x is not implemented.\n\
2. Something is implemented incorrectly.\n", pc);
	printf("Find this pc value(0x%08x) in the disassembling result to distinguish which case it is.\n\n", pc);

	assert(0);
}

/* stop temu */
make_helper(temu_trap) {

	printf("\33[1;31mtemu: HIT GOOD TRAP\33[0m at $pc = 0x%08x\n\n", cpu.pc);

	temu_state = END;

}

make_helper(bad_temu_trap) {

	printf("\33[1;31mtemu: HIT BAD TRAP\33[0m at $pc = 0x%08x\n\n", cpu.pc);

	temu_state = END;

}

make_helper(eret){
	cpu.pc = cpu.cp0.epc;
	cpu.cp0.status.EXL = 0;
	sprintf(assembly, "eret");
}

static void decode_mfc0(uint32_t instr){
	op_src1 -> type = OP_TYPE_REG;
	op_src1 -> reg = (instr & 0x0000F800) >> 11;
	
	op_src2 -> type = OP_TYPE_IMM;
	op_src2 -> imm = (instr & 0x00000007);

	op_dest-> type = OP_TYPE_REG;
	op_dest -> reg = (instr & 0x001F0000) >> 16;
}

make_helper(mfc0){
	decode_mfc0(instr);
	if(op_src1 -> reg == BadVAddr_Code && op_src2 -> imm == 0){
		reg_w(op_dest -> reg) = cpu.cp0.BadVAddr;
		sprintf(assembly, "mfc0   %s, BadVAddr,  0x%04x", REG_NAME(op_dest -> reg), op_src2 -> imm);
	}else if(op_src1 -> reg == Status_Code && op_src2 -> imm == 0){
		reg_w(op_dest -> reg) = cpu.cp0.status.val;
		sprintf(assembly, "mfc0   %s, Status,  0x%04x", REG_NAME(op_dest -> reg), op_src2 -> imm);
	}else if (op_src1 -> reg == Cause_Code && op_src2 -> imm == 0){
		reg_w(op_dest -> reg) = cpu.cp0.cause.val;
		sprintf(assembly, "mfc0   %s, Cause,  0x%04x", REG_NAME(op_dest -> reg), op_src2 -> imm);
	}else if (op_src1 -> reg == EPC_Code && op_src2 -> imm == 0){
		reg_w(op_dest -> reg) = cpu.cp0.epc;
		sprintf(assembly, "mfc0   %s, EPC,  0x%04x", REG_NAME(op_dest -> reg), op_src2 -> imm);
	}else {
		panic("[MFC0] Invalid cp0 register.\n");
	}
}

static void decode_mtc0(uint32_t instr){
	op_dest -> type = OP_TYPE_REG;
	op_dest -> reg = (instr & 0x0000F800) >> 11;
	
	op_src2 -> type = OP_TYPE_IMM;
	op_src2 -> imm = (instr & 0x00000007);

	op_src1->type = OP_TYPE_REG;
	op_src1->reg = (instr & 0x001F0000) >> 16;
}

make_helper(mtc0){
	decode_mtc0(instr);
	if(op_dest -> reg == BadVAddr_Code && op_src2 -> imm == 0){
		cpu.cp0.BadVAddr = reg_w(op_src1 -> reg);
		sprintf(assembly, "mtc0   %s, BadVAddr,  0x%04x", REG_NAME(op_src1 -> reg), op_src2 -> imm);
	}else if (op_dest -> reg == Status_Code && op_src2 -> imm == 0){
		cpu.cp0.status.val = reg_w(op_src1 -> reg);
		sprintf(assembly, "mtc0   %s, Status,  0x%04x", REG_NAME(op_src1 -> reg), op_src2 -> imm);
	}else if (op_dest -> reg == Cause_Code && op_src2 -> imm == 0){
		cpu.cp0.cause.val = reg_w(op_src1 -> reg);
		sprintf(assembly, "mtc0   %s, Cause,  0x%04x", REG_NAME(op_src1 -> reg), op_src2 -> imm);
	}else if (op_dest -> reg == EPC_Code && op_src2 -> imm == 0){
		cpu.cp0.epc = reg_w(op_src1 -> reg);
		sprintf(assembly, "mtc0   %s, EPC,  0x%04x", REG_NAME(op_src1 -> reg), op_src2 -> imm);
	}else {
		panic("[MTC0] Invalid cp0 register.\n");
	}
}